package org.liang.common.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 反射工具类.
 * 
 * <pre>
 * 提供访问私有变量,
 * 获取泛型类型Class, 
 * 提取集合中元素的属性, 
 * 转换字符串到对象等Util函数.
 * </pre>
 * 
 * @author L.Yang
 * @version 1.0, 2014-7-9
 */
public class ReflectionUtil {

    /**
     * 调用Getter方法.
     * 
     * @param target
     *            目标对象
     * @param propertyName
     *            属性名称
     * @return 方法返回值
     * @throws Exception
     *             传入非法属性名称抛出异常
     */
    public static Object invokeGetterMethod(Object target, String propertyName)
                    throws Exception {
        String getterMethodName = "get" + StringUtils.capitalize(propertyName);
        return invokeMethod(target, getterMethodName, new Class[] {}, new Object[] {});
    }

    /**
     * 调用Setter方法.
     * 
     * <pre>
     * 使用value的Class来查找Setter方法.
     * </pre>
     * 
     * @param target
     *            目标对象
     * @param propertyName
     *            属性名称
     * @param value
     *            属性值
     * @throws Exception
     *             传入非法属性名称抛出异常
     * @see
     */
    public static void invokeSetterMethod(Object target, String propertyName, Object value)
                    throws Exception {
        invokeSetterMethod(target, propertyName, value, null);
    }

    /**
     * 调用Setter方法.
     * 
     * @param propertyType
     *            用于查找Setter方法,为空时使用value的Class替代.
     * @throws Exception
     *             传入非法属性名称抛出异常
     */
    public static void invokeSetterMethod(Object target, String propertyName, Object value,
                                          Class<?> propertyType)
                    throws Exception {
        Class<?> type = propertyType != null ? propertyType : value.getClass();
        String setterMethodName = "set" + StringUtils.capitalize(propertyName);
        invokeMethod(target, setterMethodName, new Class[] {type}, new Object[] {value});
    }

    /**
     * 直接读取对象属性值.
     * 
     * <pre>
     *  无视private/protected修饰符, 不经过getter函数.
     * </pre>
     * 
     * @param object
     *            对象实例
     * @param fieldName
     *            属性名称
     * @return 返回属性值
     * @throws Exception
     *             传入非法属性名称抛出异常
     */
    public static Object getFieldValue(final Object object, final String fieldName)
                    throws Exception {
        Field field = getDeclaredField(object, fieldName);
        if (field == null) {
            String error = "Could not find field [" + fieldName + "] on target [" + object + "]";
            throw new IllegalArgumentException(error);
        }
        makeAccessible(field);
        Object result = null;
        try {
            result = field.get(object);
        } catch (IllegalAccessException e) {
            // could not happened
        }
        return result;
    }

    /**
     * 直接设置对象属性值.
     * 
     * <pre>
     * 无视private/protected修饰符, 不经过setter函数.
     * </pre>
     * 
     * @param object
     *            对象实例
     * @param fieldName
     *            属性名称
     * @param value
     *            值
     * @throws Exception
     *             传入非法属性名称抛出异常
     */
    public static void setFieldValue(final Object object,
             final String fieldName, final Object value) throws Exception {
        Field field = getDeclaredField(object, fieldName);
        if (field == null) {
            String error = "Could not find field [" + fieldName + "] on target [" + object + "]";
            throw new IllegalArgumentException(error);
        }
        makeAccessible(field);
        try {
            field.set(object, value);
        } catch (IllegalAccessException e) {
            // could not happened
        }
    }

    /**
     * 直接调用对象方法.
     * 
     * <pre>
     * 无视private/protected修饰符.
     * </pre>
     * 
     * @param object
     *            对象实例
     * @param methodName
     *            方法名
     * @param parameterTypes
     *            参数类型
     * @param parameters
     *            参数
     * @return 方法返回值
     * @throws Exception
     *             1.传入非法方法名抛出异常；2
     * @see
     */
    public static Object invokeMethod(final Object object, final String methodName,
                                      final Class<?>[] parameterTypes, final Object[] parameters)
                    throws Exception {
        Method method = getDeclaredMethod(object, methodName, parameterTypes);
        if (method == null) {
            String error = "Could not find method [" + methodName + "] on target [" + object + "]";
            throw new IllegalArgumentException(error);
        }
        method.setAccessible(true);
        try {
            return method.invoke(object, parameters);
        } catch (Exception e) {
            throw convertReflectionExceptionToUnchecked(e);
        }
    }

    /**
     * 循环向上转型.
     * 
     * <pre>
     * 获取对象的DeclaredField. 如向上转型到Object仍无法找到, 返回null.
     * </pre>
     * 
     * @param object
     *            对象实例
     * @param fieldName
     *            属性名称
     * @return DeclaredField
     * @throws Exception
     *             传入空对象 ，抛出异常
     */
    protected static Field getDeclaredField(final Object object, final String fieldName)
                    throws Exception {
        if (object == null) throw new Exception("object不能为空");
        for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass =
                        superClass.getSuperclass()) {
            try {
                return superClass.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
                // Field不在当前类定义,继续向上转型
            }
        }
        return null;
    }

    /**
     * 强行设置Field可访问.
     * 
     * @param field
     *            field对象
     */
    protected static void makeAccessible(final Field field) {
        if (!Modifier.isPublic(field.getModifiers())
            || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
            field.setAccessible(true);
        }
    }

    /**
     * 循环向上转型.
     * 
     * <pre>
     * 获取对象的DeclaredMethod. 如向上转型到Object仍无法找到, 返回null.
     * </pre>
     * 
     * @param object
     *            对象实例
     * @param methodName
     *            方法名
     * @param parameterTypes
     *            方参数
     * @return 方法实例
     * @throws Exception
     *             传入空对象 ，抛出异常
     */
    protected static Method getDeclaredMethod(Object object, String methodName,
                                              Class<?>[] parameterTypes)
                    throws Exception {
        if (object == null) throw new Exception("object不能为空");
        for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass =
                        superClass.getSuperclass()) {
            try {
                return superClass.getDeclaredMethod(methodName, parameterTypes);
            } catch (NoSuchMethodException e) {
                // Method不在当前类定义,继续向上转型
            }
        }
        return null;
    }


    /**
     * 将反射时的checked exception转换为unchecked exception.
     * 
     * @param exception
     *            checked 异常
     * @return unchecked 异常
     * @see
     */
    private static RuntimeException convertReflectionExceptionToUnchecked(Exception exception) {
        return convertReflectionExceptionToUnchecked(null, exception);
    }

    private static RuntimeException
                    convertReflectionExceptionToUnchecked(String desc,
                                                          Exception exception) {
        desc = (desc == null) ? "Unexpected Checked Exception." : desc;
        if (exception instanceof IllegalAccessException
            || exception instanceof IllegalArgumentException
            || exception instanceof NoSuchMethodException) {
            return new IllegalArgumentException(desc, exception);
        } else if (exception instanceof InvocationTargetException) {
            return new RuntimeException(
                            desc, ((InvocationTargetException) exception).getTargetException());
        } else if (exception instanceof RuntimeException) {
            return (RuntimeException) exception;
        }
        return new RuntimeException(desc, exception);
    }
}
